#include "udp_client.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <fstream>
#include <iostream>
#include "spdlog/spdlog.h"

using namespace std;
using namespace safe_udp;

UdpClient::UdpClient() {
    last_in_order_packet_ = -1;
    last_packet_received_ = -1;
    fin_flag_recvived_ = false;
}

UdpClient::~UdpClient() {
    close(sockfd_);
}

void UdpClient::SendFileRequest(const std::string& file_name) {
    int n, next_seq_expected;
    int segments_in_between = 0;
    initial_seq_number_ = 67;
    if (receiver_window_ <= 0) {
        receiver_window_ = 100;
    }
    unsigned char* buffer = (unsigned char*)calloc(MAX_PACKET_SIZE, sizeof(unsigned char));
    if (NULL == buffer) {
        spdlog::error("client buffer calloc failed");
        exit(1);
    }
    spdlog::info("server address: {}", inet_ntoa(server_address_.sin_addr));
    spdlog::info("server address port: {}", server_address_.sin_port);
    spdlog::info("server address family: {}", server_address_.sin_family);
    n = sendto(sockfd_, file_name.c_str(), file_name.size(), 0, (struct sockaddr*)&server_address_, sizeof(struct sockaddr_in));
    if (n < 0) {
        spdlog::error("Failed to write to socket");
    }
    memset(buffer, 0, MAX_PACKET_SIZE);
    std::fstream file;
    std::string file_path = string(CLIENT_FILE_PATH) + file_name;
    file.open(file_path, std::ios::out);

    while ((n = recvfrom(sockfd_, buffer, MAX_PACKET_SIZE, 0, NULL, NULL)) > 0) {
        char buffer2[20];
        memcpy(buffer2, buffer, 20);
        if (strstr("FILE NOT FOUND", buffer2) != NULL) {
            spdlog::info("file not found !!!");
            return;
        }

        std::unique_ptr<DataSegment> data_segment = std::make_unique<DataSegment>();
        data_segment->DeserializeToDataSegment(buffer, n);
        spdlog::info("packet received with seq_number_: {}", data_segment->seq_number_);

        // Random drop
        if (is_packet_drop_ && rand() % 100 < prob_value_) {
            spdlog::info("Dropping this packet with seq {}", data_segment->seq_number_);
            continue;
        }

        // Random delay
        if (is_delay_ && rand() % 100 < prob_value_) {
            int sleep_time = (rand() % 10) * 1000;
            spdlog::info("Delaying this packet with seq {} for {} us", data_segment->seq_number_, sleep_time);
            usleep(sleep_time);
        }

        if (last_in_order_packet_ == -1) {
            next_seq_expected = initial_seq_number_;
        } else {
            next_seq_expected = data_segments_[last_in_order_packet_].seq_number_ + data_segments_[last_in_order_packet_].length_;
        }

        // Old packet
        if (next_seq_expected > data_segment->seq_number_ && !data_segment->fin_flag_) {
            sendAck(next_seq_expected);
            continue;
        }

        segments_in_between = (data_segment->seq_number_ - next_seq_expected) / MAX_DATA_SIZE;
        int this_segment_index = last_in_order_packet_ + segments_in_between + 1;
        if (this_segment_index - last_in_order_packet_ > receiver_window_) {
            spdlog::info("Packet dropped {}", this_segment_index);
            continue;
        }

        if (data_segment->fin_flag_) {
            spdlog::info("Fin flag received !!!");
            fin_flag_recvived_ = true;
        }

        insert(this_segment_index, *data_segment);

        for (int i = last_in_order_packet_ + 1; i <= last_packet_received_; ++i) {
            if (data_segments_[i].seq_number_ != -1) {
                if (file.is_open()) {
                    file << data_segments_[i].data_;
                    last_in_order_packet_ = i;
                }
            } else {
                break;
            }
        }

        // 如果已经接收到 fin_flag_,且所有数据包都处理完毕,则跳出循环
        if (fin_flag_recvived_ && last_in_order_packet_ == last_packet_received_) {
            break;
        }
        sendAck(data_segments_[last_in_order_packet_].seq_number_ + data_segments_[last_in_order_packet_].length_);
        memset(buffer, 0, MAX_PACKET_SIZE);
    }
    free(buffer);
    file.close();
}

void UdpClient::CreateSocketAndServerConnection(const std::string& server_address, const std::string& port) {
}

void UdpClient::sendAck(int ack_number) {
}

void UdpClient::insert(int index, const DataSegment& data_segment) {
}

int UdpClient::addToDataSegmentVector(const DataSegment& data_segment) {
}
